package com.example.deepseek.service.impl;

import com.example.deepseek.config.DeepseekConfig;
import com.example.deepseek.model.*;
import com.example.deepseek.service.ChatSessionService;
import com.example.deepseek.service.DeepseekStreamingService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * DeepSeek流式输出服务实现
 * 使用HttpClient发送请求，并通过WebSocket将结果流式传输给客户端
 */
@Service
@Slf4j
public class DeepseekStreamingServiceImpl implements DeepseekStreamingService {

    private final DeepseekConfig deepseekConfig;
    private final SimpMessagingTemplate messagingTemplate;
    private final ChatSessionService chatSessionService;
    private final ObjectMapper objectMapper; // 注入配置好的ObjectMapper

    // 存储每个会话的累积内容
    private final Map<String, StringBuilder> sessionContentMap = new ConcurrentHashMap<>();
    // 存储会话的中断标志
    private final Map<String, Boolean> sessionStopFlags = new ConcurrentHashMap<>();

    @Autowired
    public DeepseekStreamingServiceImpl(
            DeepseekConfig deepseekConfig,
            SimpMessagingTemplate messagingTemplate,
            ChatSessionService chatSessionService,
            ObjectMapper objectMapper) {
        this.deepseekConfig = deepseekConfig;
        this.messagingTemplate = messagingTemplate;
        this.chatSessionService = chatSessionService;
        this.objectMapper = objectMapper; // 使用注入的ObjectMapper
    }

    @Override
    @Async
    public void streamMessage(String sessionId, String userMessage) {
        ChatSession session = chatSessionService.getSession(sessionId);
        if (session == null) {
            log.error("聊天会话不存在: {}", sessionId);
            return;
        }

        // 创建用户消息
        Message userMsg = new Message("user", userMessage);
        userMsg.setTimestamp(LocalDateTime.now());

        // 持久化用户消息到数据库
        chatSessionService.addMessage(sessionId, userMsg);
        session.addMessage(userMsg);

        // 重置会话的停止标志和内容累积器
        sessionStopFlags.put(sessionId, false);
        sessionContentMap.put(sessionId, new StringBuilder());

        // 准备API请求
        ChatRequest request = prepareRequest(session);

        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost(deepseekConfig.getApiUrl());

            // 添加超时设置
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(15000)    // 连接超时：15秒
                    .setSocketTimeout(20000)     // 响应超时：20秒
                    .build();
            httpPost.setConfig(requestConfig);

            httpPost.setHeader("Content-Type", "application/json");
            httpPost.setHeader("Authorization", "Bearer " + deepseekConfig.getApiKey());

            // 将请求对象转换为JSON
            String jsonRequest = objectMapper.writeValueAsString(request);
            log.debug("API请求JSON: {}", jsonRequest);
            StringEntity entity = new StringEntity(jsonRequest, ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);

            // 记录请求详情，帮助调试
            log.info("发送请求到: {}", deepseekConfig.getApiUrl());
            log.debug("请求头: {}", Arrays.toString(httpPost.getAllHeaders()));

            try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                int statusCode = response.getStatusLine().getStatusCode();
                log.debug("收到HTTP响应状态码: {}", statusCode);

                // 检查HTTP状态码
                if (statusCode != 200) {
                    String errorBody = EntityUtils.toString(response.getEntity());
                    log.error("API返回错误状态码: {}, 错误内容: {}", statusCode, errorBody);

                    // 从错误响应中提取更多信息
                    String detailedError = "未知错误";
                    try {
                        JsonNode errorJson = objectMapper.readTree(errorBody);
                        if (errorJson.has("error")) {
                            detailedError = errorJson.path("error").path("message").asText("API返回错误");
                        }
                    } catch (Exception e) {
                        detailedError = errorBody.length() > 100 ? errorBody.substring(0, 100) + "..." : errorBody;
                    }

                    // 向用户发送具体错误信息
                    String errorMessage = String.format("调用AI服务出错（状态码: %d）: %s", statusCode, detailedError);
                    handleApiError(errorMessage, sessionId);
                    return;
                }

                HttpEntity responseEntity = response.getEntity();
                if (responseEntity != null) {
                    // 添加响应处理超时检测
                    final boolean[] receivedData = {false};
                    final Timer timer = new Timer(true); // 守护线程
                    timer.schedule(new TimerTask() {
                        @Override
                        public void run() {
                            if (!receivedData[0]) {
                                log.error("API响应超时，未收到任何数据");
                                handleApiError("AI服务响应超时。请检查API配置与连接。", sessionId);
                                sessionStopFlags.put(sessionId, true); // 标记停止处理
                                timer.cancel();
                            }
                        }
                    }, 10000); // 10秒后检查是否收到数据

                    try (BufferedReader reader = new BufferedReader(
                            new InputStreamReader(responseEntity.getContent(), StandardCharsets.UTF_8))) {
                        String line;
                        int emptyLineCount = 0;
                        final int MAX_EMPTY_LINES = 50; // 最大允许的连续空行数

                        while ((line = reader.readLine()) != null) {
                            // 如果已经标记为停止，则退出循环
                            if (sessionStopFlags.getOrDefault(sessionId, false)) {
                                log.info("处理被停止，会话ID: {}", sessionId);
                                break;
                            }

                            // 检测空行，避免无限循环
                            if (line.trim().isEmpty()) {
                                emptyLineCount++;
                                if (emptyLineCount > MAX_EMPTY_LINES) {
                                    log.warn("检测到过多连续空行，可能是API响应格式问题");
                                    handleApiError("AI服务返回了不正确的响应格式。请检查API配置。", sessionId);
                                    break;
                                }
                                continue;
                            }

                            // 重置空行计数
                            emptyLineCount = 0;

                            // 标记已收到数据
                            receivedData[0] = true;

                            log.debug("收到数据行: {}", line);

                            // 处理数据行
                            if (line.startsWith("data: ")) {
                                String jsonData = line.substring(6); // 移除 "data: " 前缀

                                // 处理完成标记
                                if ("[DONE]".equals(jsonData)) {
                                    log.info("收到完成标记 [DONE]");
                                    handleComplete(sessionId);
                                    break;
                                }

                                // 处理实际数据
                                try {
                                    processJsonChunk(jsonData, sessionId);
                                } catch (Exception e) {
                                    log.error("处理数据块时出错: {}", e.getMessage(), e);
                                }
                            } else {
                                // 记录非标准格式的响应行
                                log.warn("收到非标准格式响应行: {}", line);
                            }
                        }
                    }

                    // 如果循环正常结束但未收到[DONE]标记，检查是否是因为手动停止
                    if (!sessionStopFlags.getOrDefault(sessionId, false) && receivedData[0]) {
                        log.info("响应流已结束，但未收到[DONE]标记");
                        handleComplete(sessionId);
                    }

                    timer.cancel(); // 正常完成，取消定时器
                } else {
                    handleApiError("AI服务返回了空响应。请检查API配置是否正确。", sessionId);
                }
            }
        } catch (SocketTimeoutException e) {
            log.error("API请求超时: {}", e.getMessage(), e);
            handleApiError("连接AI服务超时。请检查API URL是否正确，或服务器是否可访问。", sessionId);
        } catch (UnknownHostException e) {
            log.error("无法解析主机名: {}", e.getMessage(), e);
            handleApiError("无法连接到AI服务。请检查API URL是否正确（" + e.getMessage() + "）。", sessionId);
        } catch (Exception e) {
            log.error("流式请求处理时出错: {}", e.getMessage(), e);
            handleApiError("连接AI服务时出错: " + e.getMessage() + "。请检查API配置。", sessionId);
        } finally {
            // 确保资源被清理，即使在异常情况下
            if (sessionContentMap.get(sessionId) != null && sessionContentMap.get(sessionId).length() == 0) {
                sessionContentMap.remove(sessionId);
                sessionStopFlags.remove(sessionId);
            }
        }
    }

    /**
     * 处理API错误，向用户发送友好消息
     */
    private void handleApiError(String errorMsg, String sessionId) {
        log.error("API错误: {}", errorMsg);

        // 创建错误消息
        Message errorMessage = new Message("assistant", errorMsg);
        errorMessage.setTimestamp(LocalDateTime.now());

        // 保存到数据库
        chatSessionService.addMessage(sessionId, errorMessage);

        // 通过WebSocket通知客户端
        messagingTemplate.convertAndSend("/topic/stream/" + sessionId, "[ERROR]" + errorMsg);

        // *** 关键修改点 ***
        // 发送字符串"error"作为完成通知，与前端期望匹配
        messagingTemplate.convertAndSend("/topic/complete/" + sessionId, "error");

        // 清理会话资源
        sessionContentMap.remove(sessionId);
        sessionStopFlags.remove(sessionId);
    }

    /**
     * 处理JSON数据块
     */
    private void processJsonChunk(String jsonData, String sessionId) {
        try {
            JsonNode root = objectMapper.readTree(jsonData);
            JsonNode choices = root.path("choices");

            if (choices.isArray() && choices.size() > 0) {
                JsonNode delta = choices.get(0).path("delta");
                JsonNode content = delta.path("content");

                // 检查 content 是否为 null
                if (!content.isMissingNode() && !content.isNull()) {
                    String token = content.asText();
                    StringBuilder contentBuilder = sessionContentMap.get(sessionId);
                    contentBuilder.append(token);

                    // 发送令牌到WebSocket
                    messagingTemplate.convertAndSend("/topic/stream/" + sessionId, token);
                }
            }
        } catch (Exception e) {
            log.error("处理JSON数据块时出错", e);
        }
    }

    /**
     * 处理错误情况
     */
    private void handleError(Throwable error, String sessionId) {
        log.error("流式请求出错", error);

        Message errorMessage = new Message("assistant", "抱歉，生成回复时出错: " + error.getMessage());
        errorMessage.setTimestamp(LocalDateTime.now());
        chatSessionService.addMessage(sessionId, errorMessage);

        // 通知客户端错误
        messagingTemplate.convertAndSend("/topic/stream/" + sessionId, "[ERROR]" + error.getMessage());
        messagingTemplate.convertAndSend("/topic/complete/" + sessionId, "error");

        // 清理会话资源
        sessionContentMap.remove(sessionId);
        sessionStopFlags.remove(sessionId);
    }

    /**
     * 在DeepseekStreamingServiceImpl.java的handleComplete方法中添加以下代码
     * 放在现有方法的末尾，清理会话资源之前
     */
    private void handleComplete(String sessionId) {
        // 获取累积的完整内容
        String fullContent = sessionContentMap.getOrDefault(sessionId, new StringBuilder()).toString();
        log.info("生成完成，总字符数: {}", fullContent.length());

        // 只有当内容不为空时才创建和保存消息
        if (!fullContent.isEmpty()) {
            // 创建并保存助手消息
            Message assistantMessage = new Message("assistant", fullContent);
            assistantMessage.setTimestamp(LocalDateTime.now());
            chatSessionService.addMessage(sessionId, assistantMessage);

            // 检查是否需要自动重命名会话
            ChatSession session = chatSessionService.getSession(sessionId);
            autoRenameSessionIfNeeded(session);

            // 通知客户端生成完成
            messagingTemplate.convertAndSend("/topic/complete/" + sessionId, assistantMessage);
        } else {
            // 如果内容为空，只发送完成通知，不创建空消息
            log.warn("生成完成但内容为空，不创建消息");
            messagingTemplate.convertAndSend("/topic/complete/" + sessionId, "done");
        }

        // 清理会话资源
        sessionContentMap.remove(sessionId);
        sessionStopFlags.remove(sessionId);
    }

    /**
     * 检查并自动重命名会话（如果需要）
     * 添加到DeepseekStreamingServiceImpl类中
     */
    private void autoRenameSessionIfNeeded(ChatSession session) {
        if (session == null) return;

        // 如果会话名称是默认的"New Chat"或类似默认名称，且消息数至少有2条（用户+AI回复）
        if ((session.getName().equals("New Chat") ||
                session.getName().startsWith("对话") ||
                session.getName().equals("新对话")) &&
                session.getMessages().size() >= 2) {

            try {
                // 获取用户的第一条消息和AI的第一条回复
                Message userFirstMsg = null;
                Message aiFirstReply = null;

                for (Message msg : session.getMessages()) {
                    if (msg.getRole().equals("user") && userFirstMsg == null) {
                        userFirstMsg = msg;
                    } else if (msg.getRole().equals("assistant") && aiFirstReply == null) {
                        aiFirstReply = msg;
                    }

                    if (userFirstMsg != null && aiFirstReply != null) break;
                }

                if (userFirstMsg != null) {
                    // 根据用户第一条消息生成会话名称
                    String newSessionName = generateSessionName(userFirstMsg.getContent(), aiFirstReply != null ? aiFirstReply.getContent() : null);

                    // 更新会话名称
                    session.setName(newSessionName);
                    chatSessionService.updateSession(session);

                    // 通过WebSocket通知前端更新会话名称
                    Map<String, String> renameNotification = new HashMap<>();
                    renameNotification.put("action", "rename");
                    renameNotification.put("sessionId", session.getId());
                    renameNotification.put("newName", newSessionName);
                    messagingTemplate.convertAndSend("/topic/session/" + session.getId(), renameNotification);

                    log.info("自动重命名会话: {} -> {}", "New Chat", newSessionName);
                }
            } catch (Exception e) {
                log.error("自动重命名会话失败", e);
            }
        }
    }

    /**
     * 根据用户消息和AI回复生成会话名称
     * 添加到DeepseekStreamingServiceImpl类中
     */
    private String generateSessionName(String userMessage, String aiReply) {
        // 分析用户消息内容，生成一个简短的会话名称
        String name = userMessage;

        // 限制长度在20个字符以内
        if (name.length() > 20) {
            // 使用前20个字符并添加省略号
            name = name.substring(0, 20) + "...";
        }

        // 移除可能的换行符
        name = name.replace("\n", " ").trim();

        // 如果名称是空的，使用默认名称加时间戳
        if (name.isEmpty()) {
            name = "对话 " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("MM-dd HH:mm"));
        }

        return name;
    }

    /**
     * 准备API请求，加入文件上下文支持
     */
    private ChatRequest prepareRequest(ChatSession session) {
        ChatRequest request = new ChatRequest();
        request.setModel(deepseekConfig.getModel());
        request.setTemperature(0.7);
        request.setMax_tokens(2000);
        request.setStream(true); // 启用流式输出

        // 准备消息列表
        List<Message> apiMessages = new ArrayList<>();

        // 检查会话中是否有文件上下文
        List<Message> fileMessages = findFileMessagesInSession(session);

        // 如果有文件上下文，添加系统提示
        if (!fileMessages.isEmpty()) {
            // 创建系统提示，引导AI参考上传的文件
            StringBuilder systemPrompt = new StringBuilder();
            systemPrompt.append("你是一个有用的助手。用户已上传以下文件，请根据这些文件内容回答问题：\n\n");

            for (Message fileMsg : fileMessages) {
                systemPrompt.append("- 文件名: ").append(fileMsg.getFileName()).append("\n");

                // 添加全部文件内容（如果存在）- 优先使用文本内容
                if (fileMsg.getTextContent() != null && !fileMsg.getTextContent().isEmpty()) {
                    systemPrompt.append("文件内容:\n").append(fileMsg.getTextContent()).append("\n\n");
                } else if (fileMsg.getFileContent() != null && fileMsg.getFileContent().length > 0) {
                    try {
                        // 尝试将二进制内容转换为文本
                        String content = new String(fileMsg.getFileContent(), "UTF-8");
                        systemPrompt.append("文件内容:\n").append(content).append("\n\n");
                    } catch (Exception e) {
                        systemPrompt.append("(文件内容为二进制数据，无法显示)\n\n");
                    }
                } else {
                    systemPrompt.append("(文件内容为空或无法读取)\n\n");
                }
            }

            // 添加系统提示
            apiMessages.add(new Message("system", systemPrompt.toString()));
            log.info("添加包含文件内容的系统提示，长度: {}", systemPrompt.length());
        } else {
            // 如果没有文件，添加默认系统消息
            Message systemMessage = new Message("system", "你是一个有用的助手。");
            apiMessages.add(systemMessage);
        }

        // 添加对话历史，创建新的消息对象来避免时间戳序列化问题
        for (Message msg : session.getMessages()) {
            if ("user".equals(msg.getRole()) || "assistant".equals(msg.getRole())) {
                // 创建没有时间戳的新消息对象
                Message apiMessage = new Message(msg.getRole(), msg.getContent());
                apiMessages.add(apiMessage);
            }
        }

        request.setMessages(apiMessages);
        return request;
    }

    /**
     * 从会话中查找包含文件内容的系统消息，并尝试从文件系统加载文件内容
     */
    private List<Message> findFileMessagesInSession(ChatSession session) {
        List<Message> fileMessages = session.getMessages().stream()
                // 修改过滤条件，确保正确识别system角色的文件消息
                .filter(msg -> "system".equals(msg.getRole()) && msg.getFileName() != null)
                .collect(Collectors.toList());

        log.info("在会话中找到 {} 条包含文件内容的消息", fileMessages.size());

        // 处理找到的每条文件消息
        for (Message msg : fileMessages) {
            // 如果文件内容为空，但文件路径存在，尝试从文件系统加载
            if ((msg.getTextContent() == null || msg.getTextContent().isEmpty()) &&
                    (msg.getFileContent() == null || msg.getFileContent().length == 0) &&
                    msg.getFilePath() != null && !msg.getFilePath().isEmpty()) {

                try {
                    // 尝试从文件系统读取文件
                    log.info("尝试从文件系统读取文件: {}", msg.getFilePath());
                    File file = new File(msg.getFilePath());
                    if (file.exists() && file.length() > 0) {
                        // 读取文件内容
                        String fileContent = readFileContent(file);
                        if (fileContent != null && !fileContent.isEmpty()) {
                            msg.setTextContent(fileContent);
                            log.info("成功从文件系统加载文件内容，长度: {}", fileContent.length());
                        }
                    }
                } catch (Exception e) {
                    log.error("从文件系统读取文件内容失败: {}", e.getMessage());
                }
            }

            // 检查是否有文本内容
            String content = msg.getTextContent() != null ? msg.getTextContent() :
                    (msg.getFileContent() != null ? "二进制内容" : "无内容");
            log.info("文件消息: 文件名={}, 内容类型={}, 内容长度={}",
                    msg.getFileName(),
                    msg.getTextContent() != null ? "文本" : "二进制",
                    content != null ? (content.equals("二进制内容") ? "二进制" : content.length()) : 0);
        }
        return fileMessages;
    }

    /**
     * 从文件读取文本内容
     * 支持常见文件类型的文本提取，兼容Java 8
     */
    private String readFileContent(File file) {
        String fileName = file.getName().toLowerCase();
        try {
            if (fileName.endsWith(".pdf")) {
                return extractPdfContent(file);
            } else if (fileName.endsWith(".txt") || fileName.endsWith(".csv") || fileName.endsWith(".md")) {
                return readTextFile(file);
            } else if (fileName.endsWith(".docx") || fileName.endsWith(".doc")) {
                return extractDocContent(file);
            } else {
                // 尝试作为普通文本文件读取
                return readTextFile(file);
            }
        } catch (Exception e) {
            log.error("读取文件内容失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 读取文本文件内容，兼容Java 8
     */
    private String readTextFile(File file) throws IOException {
        StringBuilder content = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(file), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
        }
        return content.toString();
    }

    /**
     * 提取PDF文件内容
     */
    private String extractPdfContent(File file) {
        try {
            PDDocument document = PDDocument.load(file);
            PDFTextStripper stripper = new PDFTextStripper();
            String text = stripper.getText(document);
            document.close();
            return text;
        } catch (Exception e) {
            log.error("提取PDF内容失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 提取Word文档内容
     */
    private String extractDocContent(File file) {
        try {
            if (file.getName().toLowerCase().endsWith(".docx")) {
                XWPFDocument document = new XWPFDocument(new FileInputStream(file));
                XWPFWordExtractor extractor = new XWPFWordExtractor(document);
                String text = extractor.getText();
                extractor.close();
                return text;
            } else {
                HWPFDocument document = new HWPFDocument(new FileInputStream(file));
                WordExtractor extractor = new WordExtractor(document);
                String text = extractor.getText();
                extractor.close();
                return text;
            }
        } catch (Exception e) {
            log.error("提取Word文档内容失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 停止生成
     */
    @Override
    public void stopGeneration(String sessionId) {
        log.info("请求停止生成, 会话ID: {}", sessionId);
        sessionStopFlags.put(sessionId, true);
    }
}